<?php
/**
 * lib for the GraphSpace, Graph Interpreter Project: nth-graph. 
 * @copyright Copyright (c) 2009-2010 Nate Morse
 * @name nthGraph base class
 * @author Nate Morse <morse.nate@gmail.com>
 * @version 0.03
 * @package nth-graph
 */


/**
 * @package nth-graph
 */
class nthGraph {
	var $graph;
    var $environment;
	/**
	 * @param string $graphName file path or location of the graph
	 * Reads in a graph by calling loadGraph()
	 */	 	
	function __construct($graphName) {
		// read in the graph.
		$this->graph = $this->loadGraph($graphName);
	}

	function warn($message) {
		echo "<br/>"; echo $message;
	}
	function debug($message) {
		//echo "<br/>"; echo $message;
	}
	function debug_data($message, $d) {
		//echo "<br/>"; echo $message; echo "<pre>"; var_dump($d); echo "</pre>";
	}
	/**
	 * @param string $path filename or URL or SQL statment used to get a graph
	 * @param string $method indicates the type of path, defaults to 'file'
	 * @return array|false return false if the graph cannot be loaded
	 */	 	
	function loadGraph($path, $method='file') {
		if (!(file_exists($path))) {
			$this->warn("failed to open graph resource ".$path);
			die ("Error" . " File: " . __FILE__ . " on line: " . __LINE__);
		}
		$this->debug("opening ".$path);
		// read the entire contents of the file into string
		$gJsonString = file_get_contents($path);
		$gArr = json_decode($gJsonString, true);
		if ($gArr == null) {
			$this->warn("failed to decode graph resource ");
			$this->debug($gJsonString);
			return false;
		}
		//var_dump($gArr);
		return $gArr;
	}

	/**
	 * @param int $node_index index of the base node
	 * @param string $type the type of edge to be filtered
	 * @return array array of out going edges
	 */	 
	function getOutEdges($node_index, $type) {
		$outEdges = array();
		foreach($this->graph['edges'] as $edge_index=>$edge_obj) {
			if ($edge_obj['from'] == $node_index && $edge_obj['type'] == $type) {
				array_push($outEdges, $this->graph['edges'][$edge_index]);
			} 
		}
		//echo ("<br />node_index = " & $node_index & " # "&count($outEdges));
		return $outEdges;
	}
	
	/**
	 * @param string $type the type of nodes to be filtered
	 * @return array array of nodes
	 */	 
	function getNodesOfType($type) {
		$nodes = array();
		foreach($this->graph['nodes'] as $node_index=>$node_obj) {
			if (array_key_exists($type, $node_obj) && $node_obj['type'] == $type) {
				array_push($nodes, $node_obj);
			} 
		}
		//echo ("<br />node_index = " & $node_index & " # "&count($outEdges));
		return $nodes;
	}


	/**
	 * @param int $node_index index of the node
	 * @return array Returns the process 
	 */	 
	function getNodeProcess($node_index) {
		if (!array_key_exists($node_index, $this->graph['nodes'])) return 'NoOp 1';
		if (!array_key_exists('process', $this->graph['nodes'][$node_index])) return 'NoOp 2';
		$process = $this->graph['nodes'][$node_index]['process'];
		//echo "<br/>process = ";
		//var_dump($process);
		return $process;
	}
	
	/**
	 * @param int $node_index index of the node
	 * @param string $attr the node attribute
	 * @return mixed Returns the nodes attribute 
	 */	 
	function getNodeAttr($node_index, $attr) {
		if (!array_key_exists($node_index, $this->graph['nodes'])) return 'no '.$node_index.' fault_1';
		if (!array_key_exists($attr, $this->graph['nodes'][$node_index])) return 'no '.$attr.' fault_2';
		$dataSet = $this->graph['nodes'][$node_index][$attr];
		//echo "<br/>getNodeAttr($node_index, $attr)";
		//echo "<br/>$dataSet = ";
		//var_dump($dataSet);
		return $dataSet;
	}
	
	/**
	 * @param int $node_index index of the node
	 * @return mixed Returns the attribute 'data' for the node
	 */	 
	function getNodeData($node_index) {
		if (!array_key_exists($node_index, $this->graph['nodes'])) {
            echo 'no node index ('.$node_index.') error_1 ';
            return null;
        }
		if (!array_key_exists('data', $this->graph['nodes'][$node_index])) {
            $edges = $this->getOutEdges($node_index, 'get');
            $data = null;
            // search all get edges to find data 
    		foreach($edges as $e_i=>$edge) {
    		  $data = $this->_array_merge_recursive2($this->getNodeData($edge['to'], $data));
    		  
    		}
    		if($data === null) {
                echo 'no data error_2 ';
                return null;
            }
        }
		$dataSet = $this->graph['nodes'][$node_index]['data'];
		//echo "<br/>getNodeData($node_index, $attr)";
		//echo "<br/>$dataSet = ";
		//var_dump($dataSet);
		return $dataSet;
	}

	/**
	 * @param int $node_index index of the node
	 * @param string $attr the node attribute
	 * @return mixed Returns the node, data attribute 
	 */	 	
	function getNodeDataAttr($node_index, $attr) {
		$dataSet = $this->getNodeData($node_index);
		//var_dump($dataSet);
		if ($dataSet === null || !array_key_exists($attr, $dataSet)) return 'error_3 - no '.$attr;
		return $dataSet[$attr];
	}
	
	/**
	 * @param int $node_index index of the node
	 * @param string $attr the node attribute
	 * @param string $value the node attribute
	 */
	function setNodeDataAttr($node_index, $attr, $value) {
		//echo "<br/>setNodeDataAttr($node_index, $attr, $value)";
		$this->graph['nodes'][$node_index]['data'][$attr] = $value;
	}
	
	/**
	 * Sets data in input nodes in prepration for a traversal. 
	 */
	function setInputs() {
		foreach($this->graph['nodes'] as $node_index=>$node_obj) {
			if (array_key_exists('type', $node_obj) && $node_obj['type'] == 'in') {
				if (array_key_exists('subtype', $node_obj) && $node_obj['subtype'] == 'GET')
					$this->graph['nodes'][$node_index]['data'][$node_obj['name']] = $_GET[$node_obj['name']];
				elseif (array_key_exists('subtype', $node_obj) && $node_obj['subtype'] == 'POST')
					$this->graph['nodes'][$node_index]['data'][$node_obj['name']] = $_POST[$node_obj['name']];
				else
					$this->graph['nodes'][$node_index]['data'][$node_obj['name']] = $_REQUEST[$node_obj['name']];
			}
		}
	}
	
	/**
	 * Gets data from adjacent nodes [graphs]
	 * @return array data from all out going 'get' edges	 
	 */	 	
	function get($node_index) {
		$data = array();
		$outEdges = $this->getOutEdges($node_index, "get");
		foreach($outEdges as $edge_index=>$edge_obj) {
			$edge_name = $edge_obj['name'];
			$edge_alias = $edge_obj['alias'];
			$to_node_index = $edge_obj['to'];
			$sub_type = $edge_obj['subtype'];
			if ($sub_type == "run-process") {
				$this->process($to_node_index);
			}
			if (!$edge_name && !$edge_alias) {
				$data = $this->_array_merge_recursive2($data, $this->getNodeData($to_node_index));
			}
			else { 
				if (!$edge_name) {
					$edge_name = $this->getNodeAttr($to_node_index, 'name');
				}
				if (!$edge_alias) { 
					$edge_alias = $edge_name;
				}
				$data[$edge_name] = $this->getNodeDataAttr($to_node_index, $edge_alias);
			}
		}
		//echo "<br/>get() data = "; var_dump($data);
		return $data;
	}

	
	/**
	 * @param int $node_index index of the node
	 * @param array $data data to be merged with existing data
	 */
	function mergeNodeData($node_index, $data, $cache=false) {
		$this->graph['nodes'][$node_index]['data'] = 
			$this->_array_merge_recursive2(
				  $this->graph['nodes'][$node_index]['data']
				, $data
			);
	}
	
	/**
	 * @param array $paArray1 object to be merged into
	 * @param array $paArray1 object to be merged from
	 * @return array merged object 
	 */
	function _array_merge_recursive2($paArray1, $paArray2) {
		if (!is_array($paArray1) or !is_array($paArray2)) { return $paArray2; }
		foreach ($paArray2 AS $sKey2 => $sValue2)
		{
			$paArray1[$sKey2] = $this->_array_merge_recursive2(@$paArray1[$sKey2], $sValue2);
		}
		return $paArray1;
	}
}

?>
